# -*- coding: utf-8 -*-

import copy

from data_load.data_load import train_data, test_data


def get_neighbors(user, k):
    """
        找出user相似的k个邻居
    :param user: 目标user
    :param k: 邻居个数
    :return:k个邻居数组
    """
    neighbors = []
    distances = []
    best_k_neighbors = []

    users = list(train_data.user_events.keys())
    users.remove(user)
    for cur_user in users:
        cur_distance = cal_distance(user, cur_user)
        neighbors.append(cur_user)
        distances.append(cur_distance)
    neighbors_index = get_k_max(distances, k)

    for index in neighbors_index:
        best_k_neighbors.append(neighbors[index])
    return best_k_neighbors


def get_k_max(l: list, k: int) -> list:
    """
        对给定的list，返回其中值排名前k的数的下标集合
    :param l:
    :param k:
    :return:
    """
    key = [i for i in range(k)]
    value = [l[i] for i in range(k)]
    min_l = min(value)
    if k > len(l):
        return [i for i in range(len(l))]
    for index in range(k, len(l)):
        if l[index] > min_l:
            index_min = value.index(min_l)  # 当前value中的k个元素最小值的下标
            value[index_min] = l[index]
            key[index_min] = index
            min_l = min(value)

    # 按value的值大小对key进行对应位置的排序
    key = sort_list_by_list(key, value)
    return key


def sort_list_by_list(list_1, list_2: list):
    """
        根据list_2的值对list_1进行排序
    :param list_1:
    :param list_2:
    :return: 排序后的list1
    """
    list_1_2 = []
    min_2 = min(list_2) - 1
    list_2_2 = copy.deepcopy(list_2)
    list_2_2.sort(reverse=True)
    for i in list_2_2:
        index = list_2.index(i)
        list_2[index] = min_2
        list_1_2.append(list_1[index])
    return list_1_2


def cal_distance(user1, user2) -> float:
    """
        计算两个用户的距离
        公式：取两个user的交互item a,b
            distance=(a|b)/(a&b)
    :param user1:
    :param user2:
    :return:返回两个user的相似度
    """
    items_1 = train_data.user_events[user1]
    items_2 = train_data.user_events[user2]
    if len(items_1 & items_2) == 0 or len(items_1 | items_2) == 0:
        return 0
    distance = len(items_1 & items_2) / len(items_1 | items_2)
    return distance


def recommend_item(user, num):
    """
        给user推荐物品，将相似的user的物品进行推荐
    :param user:要推荐的目标用户
    :param num:需要推荐的item数
    :return:推荐的item列表
    """
    if user not in train_data.user_events:
        return []
    neighbors = get_neighbors(user, k)
    item_set = set()
    for neighbor in neighbors:
        if len(item_set) == num:
            break
        cur_items = train_data.user_events[neighbor]
        for item in cur_items:
            item_set.add(item)
            if len(item_set) == num:
                break

    return list(item_set)


if __name__ == '__main__':
    k = 10
    events = test_data.events[:3000]
    length = len(events)
    for RH_num in [5, 10, 20]:
        true_recommend = 0
        for i in range(length):
            print('\r进度：', round(i * 100 / length, 2), '%  当前第', i, '个， 共', length, '个', end='')
            print('  已推荐正确个数', true_recommend, end='')
            event = events[i]
            cur_user = event.user
            cur_item = event.item
            items_recommend = recommend_item(cur_user, RH_num)
            if cur_item in items_recommend:
                true_recommend += 1

        RH = round(true_recommend * 100 / len(events), 2)
        print('\r', 'RH@', RH_num, ' :', RH, '%  推荐正确个数:', true_recommend, '  总数:', len(events))
